#include <stdio.h>
#include <stdlib.h>
#include "ip.h"
#include "copetcpsink.h"

static class copeTcpSinkClass : public TclClass {
public:
    copeTcpSinkClass() : TclClass("Agent/TCPSink/copeTcpSink") {}
    TclObject* create(int, const char*const*) {
	return (new copeTcpSink(new Acker));
    }
} class_copetcpsink;

void copeTcpSink::recv(Packet* pkt, Handler*)
{	
	int numToDeliver;
	int numBytes = hdr_cmn::access(pkt)->size();
	
	if(openfile==1){
		pkt_received+=1;
		fprintf(tFile,"%-16d %-16f %-16d\n", pkt_received, Scheduler::instance().clock(), numBytes);
	}
	
	// number of bytes in the packet just received
	hdr_tcp *th = hdr_tcp::access(pkt);
	/* W.N. Check if packet is from previous incarnation */
	if (th->ts() < lastreset_) {
	    // Remove packet and do nothing
	    Packet::free(pkt);
	    return;
	}

	if(th->seqno() == acker_->last_ack_sent_ + 1) {
	    TcpSink::recv(pkt, 0);
	    
	    if(min_seqno == th->seqno() + 1) {
		map<int, Packet*>::iterator it;
		while((it = pktmap.find(min_seqno)) != pktmap.end()) {
		    TcpSink::recv(pkt, 0);
		    min_seqno ++;
		}
		if(pktmap.empty())
		    min_seqno = 0;
		else
		    min_seqno = (*pktmap.begin()).first;
	    }
	    return;
	} else if(th->seqno() > acker_->last_ack_sent_ + 1) {
	    min_seqno = th->seqno();
	    pktmap[th->seqno()] = pkt;
	    return;
	}

	acker_->update_ts(th->seqno(),th->ts(),ts_echo_rfc1323_);
	// update the timestamp to echo
	
      	numToDeliver = acker_->update(th->seqno(), numBytes);
	// update the recv window; figure out how many in-order-bytes
	// (if any) can be removed from the window and handed to the
	// application
	if (numToDeliver) {
		bytes_ += numToDeliver;
		recvBytes(numToDeliver);
	}
	// send any packets to the application
      	ack(pkt);
	// ACK the packet
	Packet::free(pkt);
	// remove it from the system
}

int copeTcpSink::command(int argc, const char*const* argv)
{	
	Tcl& tcl = Tcl::instance();
	if (strcmp(argv[1], "set_filename") == 0) {
		strcpy(tbuf, argv[2]);
		tFile = fopen(tbuf, "w");
		openfile=1;
		return (TCL_OK);
	} 
	
	if (strcmp(argv[1], "closefile") == 0) {	
		fclose(tFile);
		return (TCL_OK);
	}
	
	return (TcpSink::command(argc, argv));
}
